package com.songyang.yougnsql.core.parser;

import com.songyang.yougnsql.common.ErrorMsg;
import com.songyang.yougnsql.common.YoungSQLException;

/**
 * @ClassName Tokenizer
 * @Description SQL分词器
 * @date 2022/12/28 17:06
 * @Author yanceysong
 * @Version 1.0
 */
public class Tokenizer {
    /**
     * 指令的字节
     */
    private final byte[] stat;
    private int pos;
    private String currentToken;
    /**
     * 需要重新获取一个token
     */
    private boolean flushToken;
    private Exception err;

    public Tokenizer(byte[] stat) {
        this.stat = stat;
        this.pos = 0;
        this.currentToken = "";
        this.flushToken = true;
    }

    /**
     * 判断字符是否是数字
     *
     * @param b 字符
     * @return 结果
     */
    private static boolean isDigit(byte b) {
        return (b >= '0' && b <= '9');
    }

    /**
     * 判断字符是否是字母
     *
     * @param b 字符
     * @return 结果
     */
    public static boolean isAlphaBeta(byte b) {
        return ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z'));
    }

    /**
     * 判断字符是否是符号
     *
     * @param b 待判断的字符
     * @return 判断结果
     */
    public static boolean isSymbol(byte b) {
        return (b == '>' || b == '<' || b == '=' || b == '*' ||
                b == ',' || b == '(' || b == ')');
    }

    /**
     * 判断字符是否是空的
     *
     * @param b 待判断的字符
     * @return 判断结果
     */
    public static boolean isBlank(byte b) {
        return (b == '\n' || b == ' ' || b == '\t');
    }

    /**
     * 看一下即将出队的元素
     *
     * @return 即将出队的元素
     * @throws Exception 异常
     */
    public String peek() throws YoungSQLException {
        try {
            if (err != null) {
                throw err;
            }
            if (flushToken) {
                String token = null;
                try {
                    token = next();
                } catch (Exception e) {
                    err = e;
                    throw e;
                }
                currentToken = token;
                flushToken = false;
            }
            return currentToken;
        } catch (Exception e) {
            if (e instanceof YoungSQLException) {
                throw new YoungSQLException(e.getMessage() + "near " + currentToken);
            }
            e.printStackTrace();
        }
        return "";

    }

    /**
     * 出队一个元素
     */
    public void pop() {
        flushToken = true;
    }

    /**
     * 返回解析SQL的错误数据
     *
     * @return 数据
     */
    public byte[] errStat() {
        byte[] res = new byte[pos + 2];
        System.arraycopy(stat, 0, res, 0, pos);
        System.arraycopy("<-".getBytes(), 0, res, pos, 2);
//        System.arraycopy(stat, pos, res, pos + 2, stat.length - pos);
        return res;
    }

    /**
     * 出队一个
     */
    private void popByte() {
        pos++;
        if (pos > stat.length) {
            pos = stat.length;
        }
    }

    /**
     * 查看一个byte
     *
     * @return byte
     */
    private Byte peekByte() {
        if (pos == stat.length) {
            return null;
        }
        return stat[pos];
    }

    /**
     * 获取下一个token
     *
     * @return 下一个token
     * @throws Exception 异常
     */
    private String next() throws Exception {
        if (err != null) {
            throw err;
        }
        return nextMetaState();
    }

    /**
     * 获取下一个token
     *
     * @return token
     * @throws Exception 异常
     */
    private String nextMetaState() throws Exception {
        while (true) {
            Byte b = peekByte();
            if (b == null) {
                return "";
            }
            if (!isBlank(b)) {
                break;
            }
            popByte();
        }
        byte b = peekByte();
        if (isSymbol(b)) {
            popByte();
            return new String(new byte[]{b});
        } else if (b == '"' || b == '\'') {
            return nextQuoteState();
        } else if (isAlphaBeta(b) || isDigit(b)) {
            return nextTokenState();
        } else {
            err = new YoungSQLException(ErrorMsg.INVALID_COMMAND_EXCEPTION);
            throw err;
        }
    }

    private String nextTokenState() throws Exception {
        StringBuilder sb = new StringBuilder();
        while (true) {
            Byte b = peekByte();
            if (b == null || !(isAlphaBeta(b) || isDigit(b) || b == '_')) {
                if (b != null && isBlank(b)) {
                    popByte();
                }
                return sb.toString();
            }
            sb.append(new String(new byte[]{b}));
            popByte();
        }
    }

    private String nextQuoteState() throws Exception {
        byte quote = peekByte();
        popByte();
        StringBuilder sb = new StringBuilder();
        while (true) {
            Byte b = peekByte();
            if (b == null) {
                err = new YoungSQLException(ErrorMsg.INVALID_COMMAND_EXCEPTION);
                throw err;
            }
            if (b == quote) {
                popByte();
                break;
            }
            sb.append(new String(new byte[]{b}));
            popByte();
        }
        return sb.toString();
    }
}
